#ifndef MOCHA_DETAIL_REGION_HPP_INCLUDED
#define MOCHA_DETAIL_REGION_HPP_INCLUDED

/**
 * @file
 */

#include <cmath>
#include <iostream>
#include <cml/cml.h>

namespace mocha {
/**
 * Rectangular region.
 * @param T 
 */
template<typename T>
struct region {
	region(): x(0), y(0), w(0), h(0) {}
	/**
	 * @param x 
	 * @param y 
	 * @param w 
	 * @param h 
	 */
	region(const T& x, const T& y, const T& w, const T& h): x(x), y(y), w(w), h(h) {}
	template<typename U>
	region(const region<U>& other): x(static_cast<T>(other.x)), y(static_cast<T>(other.y)), w(static_cast<T>(other.w)), h(static_cast<T>(other.h)) {}
	template<typename U>
	region<T>& operator=(const region<U>& rhs) {
		x = static_cast<T>(rhs.x);
		y = static_cast<T>(rhs.y);
		w = static_cast<T>(rhs.w);
		h = static_cast<T>(rhs.h);
		return *this;
	}
	/// 
	T x, y, w, h;
};
/**
 * Sets all components of a region to zero.
 * @param T 
 * @param r 
 */
template<typename T>
void zero(region<T>& r) {
	r.x = r.y = r.w = r.h = 0;
}
/**
 * @param T 
 * @param r Region.
 * @return True if the region is square, otherwise false.
 */
template<typename T>
bool square(const region<T>& r) {
	static float epsilon = 0.001f;
	return std::fabs(static_cast<float>(r.w - r.h)) < epsilon;
}
/**
 * @param T 
 * @param U 
 * @param r Region.
 * @return Area of the region.
 */
template<typename T, typename U = T>
U area(const region<T>& r) {
	return r.w * r.h;
}
/**
 * @param T 
 * @param r Region.
 * @return The top of the region.
 */
template<typename T>
const T& top(const region<T>& r) {
	return r.y;
}
/**
 * Sets the top of a region.
 * @param T 
 * @param r Region.
 * @param t Top.
 * @return The (new) top of the region.
 */
template<typename T>
T/*&*/ top(region<T>& r, const T& t) {
	return r.y = t;
}
/**
 * @param T 
 * @param r Region.
 * @return The left of the region.
 */
template<typename T>
const T& left(const region<T>& r) {
	return r.x;
}
/**
 * Sets the left side of the region.
 * @param T 
 * @param r Region.
 * @param l Left.
 * @return The (new) left of the region.
 */
template<typename T>
T/*&*/ left(region<T>& r, const T& l) {
	return r.x = l;
}
/**
 * @param T 
 * @param r Region.
 * @return The bottom of the region.
 */
template<typename T>
const T bottom(const region<T>& r) {
	return r.y + r.h;
}
template<typename T>
T bottom(region<T>& r, const T& b) {
	r.h = b - r.y;
	return b;
}
template<typename T>
const T right(const region<T>& r) {
	return r.x + r.w;
}
template<typename T>
T right(region<T>& r, const T& g) {
	r.w = g - r.x;
	return g;
}
template<typename T, typename U>
bool contains(const region<T>& r, const cml::vector<U, cml::fixed<2> >& p) {
	return (left(r) < p[0]) && (right(r) > p[0]) && (top(r) < p[1]) && (bottom(r) > p[1]);
}
/**
 * @param T 
 * @param r First region.
 * @param q Second region.
 * @return 
 */
template<typename T>
region<T> join_minor(const region<T>& r, const region<T>& q) {
	return region<T>(std::max(left(r), left(q)), std::max(top(r), top(q)), std::max(left(r), left(q)) - std::min(right(r), right(q)), std::max(top(r), top(q)) - std::min(bottom(r), bottom(q))); 
}
/**
 * @param T 
 * @param r First region.
 * @param q Second region.
 * @return 
 */
template<typename T>
region<T> join_major(const region<T>& r, const region<T>& q) {
	return region<T>(std::min(left(r), left(q)), std::min(top(r), top(q)), std::max(right(r), right(q)) - std::min(left(r), left(q)), std::max(bottom(r), bottom(q)) - std::min(top(r), top(q)));
}
template<typename T>
std::ostream& operator<<(std::ostream& lhs, const region<T>& rhs) {
	return lhs << "(" << left(rhs) << ", " << top(rhs) << ") x (" << right(rhs) << ", " << bottom(rhs) << ")";
}
}

#endif

